package egs.comms;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.net.*;

import egs.packets.*;


public abstract class Connection {

    protected ByteBuffer rcvBuf; //Holder for packet chunks
    protected DataOutputStream outs;
    protected DataInputStream ins;
    protected Socket socket; 
    protected int SO_TIMEOUT = 30000;
      
    

    /*
     * Construct a connection object to a client
     */
    public Connection( int bufSize ) {
    	rcvBuf = ByteBuffer.allocate( bufSize );
    }

    public Connection(Socket socket)throws IOException{
    	this.socket = socket;
    	//this.socket.setSoTimeout(SO_TIMEOUT);
    	this.socket.setSoLinger(true, SO_TIMEOUT);
    	setStreams(); 
    }
    
       
    public void setStreams() throws IOException{
    	this.outs = new DataOutputStream(socket.getOutputStream());
    	this.ins = new DataInputStream(socket.getInputStream());
    }
    
    public void setStreams( DataOutputStream outs, 
			    DataInputStream ins ) {
    	this.outs = outs;
    	this.ins = ins;
    }
	
    /*
     * Send the packet out on the stream.
     *
     * @param p - The packet to send
     * @returns true on success, false on failure
     */
    public void sendPacket( Packet p ) throws EGSCommunicationException {
    	boolean retVal = true;
    	byte[] bytesToSend = p.getRaw();
    	try{
    		this.outs.write(bytesToSend, 0, bytesToSend.length);
    	}catch(IOException ioe){
    		System.out.println("Could not send data " +  ioe);
    		retVal = false;
    		throw new EGSCommunicationException(ioe.getMessage());
    		
    	}
    	
    	
    }

    /*
     * Get the next available packet off the stream.
     * 
     * @returns The next available packet, null on failure
     */
    public Packet getPacket() throws InvalidPacketException {
    	byte[] packetData = null;
    	Packet packet = null; 
    	byte[] header = new byte[Packet.HEADER_SIZE];
    	int dataSize = 0;
    	int dataBytesRead = 0;
    	    	
    	try{
    		//need to read in the first 12 bytes of the header
    		this.ins.read(header, 0, Packet.HEADER_SIZE);
    		//the last 4 bytes read in are size of the data to follow, make an integer out of them:
    		dataSize = ((header[8] & 0xff) << 32) | ((header[9] & 0xff) << 16) | ((header[10] & 0xff) << 8) | (header[11] & 0xff);
    
    		 packetData = new byte[Packet.HEADER_SIZE + dataSize];
    		 for(int i = 0; i < Packet.HEADER_SIZE; i++){
    			packetData[i] = header[i]; 
    		 }
    		 dataBytesRead = this.ins.read(packetData, Packet.HEADER_SIZE, dataSize);
		    if(dataBytesRead != dataSize){
		    	throw new InvalidPacketException("Packet lenght does not match data received");
		    }
    	}catch(IOException e){
    		throw new InvalidPacketException("Could not create packet, exception: " + e);
    	}
    	packet = Packet.getNextPacket(packetData);
	    return packet; 
	}

    public void closeConnection(){
    	try {
    		this.ins.close();
    		this.outs.close();
			socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
    
    public void sendBytes(byte[] data) throws IOException{
    	this.outs.write(data);
    }
    
}